Skip to content

Store each action screenshot once in the trace zip - #508

Merged
vishnuv688 merged 1 commit into
mainfrom
perf/495-frame-snapshot-dedupe
Nov 2, 2026
Merged

vishnuv688 merged 1 commit into
mainfrom
perf/495-frame-snapshot-dedupe

Conversation

@vishnuv688

@vishnuv688 vishnuv688 commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

What & why

Closes #495. Part of #494. Stacked on #507.

Every per-action frame-snapshot event carried its screenshot again as base64, inside an HTML page wrapped around an <img>, while the same screenshot was already stored under resources/. On the WDIO cucumber example trace that was 529 KB of trace.trace's 538 KB zipped. The snapshot's <img> now points at a URL that the snapshot's resourceOverrides maps to the stored file, so each screenshot is stored once.

Type of change

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Polish (an improvement to an existing feature)
  • Breaking change (existing behavior changes for users)
  • Documentation
  • Internal (build, CI, dependencies, tooling)

Packages touched

  • shared (types and contracts)
  • core (framework-agnostic capture/reporting)
  • elements (published element/snapshot API — @wdio/elements)
  • service (WebdriverIO adapter)
  • nightwatch-devtools (Nightwatch adapter)
  • selenium-devtools (Selenium adapter)
  • selenium-devtools-py (Selenium Python adapter)
  • backend (server)
  • app (UI)
  • script (page-injected runtime)
  • trace (Trace mode)

Capability matrix

  • packages/conformance/src/features.ts updated, and tables regenerated with pnpm docs:capabilities, if this adds or changes user-facing behaviour

Not needed: traces open and replay as before; only their size changes.

Notes for reviewers

Measured

Before After
Zip 1,631,656 B 1,098,393 B (33% smaller)
trace.trace zipped 538,011 B 4,748 B

Rebuilt from the same capture with the old and new code; the old-code rebuild was byte-identical to the original zip.

Design notes

  • The <img> points at https://screenshot.wdio-devtools.invalid/action.<png|jpeg>. One function now names the screenshot file for the writer, the filmstrip and the override, so they cannot drift apart.
  • The frame-snapshots exist for other viewers of the format; our player replays from the mutation stream and never read them. Playwright's viewer (which Allure embeds) applies an override only to a resource recorded before the snapshot (snapshotRenderer.ts resourceByUrl: the loop stops at a resource with _monotonicTime >= snapshot.timestamp, and the override is applied only if (result && GET)). So each image type gets one synthetic resource-snapshot at _monotonicTime: -1.
  • The .invalid host never resolves (RFC 6761), so a viewer without override support fetches nothing.
  • Why not core: this is trace-format writing, which lives in trace; no adapter changes.

Testing

  • pnpm build, pnpm test (3290 passed) and pnpm lint are green; no workspace import leaks in the adapter or backend bundles.
  • New tests: no data: or base64 in frame-snapshot events; every override and resource response names a file that exists in a real exported zip; our reader round-trips commands, filmstrip and a11y text unchanged and lists no synthetic request in the Network tab.

Not verified yet

  • Opening a new zip in Playwright's viewer (trace.playwright.dev and npx playwright show-trace) and in Allure 2.35+, stepping through actions to confirm each shows its own screenshot. This needs a browser.
  • Playwright's Network tab will list the synthetic action.png request once per image type. Our Network tab does not.

Screenshots / recordings

Not applicable: no UI change in our player. Screenshots from Playwright's viewer and Allure belong here once the check above is done.

🤖 Generated with Claude Code

@vishnuv688 vishnuv688 self-assigned this Oct 29, 2026
vishnuv688 added a commit that referenced this pull request Oct 30, 2026
This branch's first CI run measured every example with #508's dedupe in
place: WDIO 966,841 bytes, Selenium 158,349, Nightwatch 260,322 and Python
688,296. Each budget is that total plus 25%, so the saving cannot be undone
without failing the job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every per-action frame-snapshot carried its screenshot again as base64,
beside the same image under resources/. On the WDIO cucumber example that
was 529 KB of trace.trace's 538 KB. The snapshot's <img> now points at a
URL that a resource override maps to the stored file, which takes that zip
from 1.63 MB to 1.10 MB.

The frame-snapshots exist for other viewers of the format. Playwright's
viewer applies an override only to a resource recorded before the
snapshot, so each image type gets one synthetic resource-snapshot at
_monotonicTime -1. The URL is on a .invalid host, so a viewer without
override support fetches nothing. Our own player replays from the mutation
stream and never read these events.

Closes #495

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vishnuv688
vishnuv688 force-pushed the perf/495-frame-snapshot-dedupe branch from c5b36b2 to e50c3b0 Compare October 30, 2026 10:12
vishnuv688 added a commit that referenced this pull request Oct 30, 2026
This branch's first CI run measured every example with #508's dedupe in
place: WDIO 966,841 bytes, Selenium 158,349, Nightwatch 260,322 and Python
688,296. Each budget is that total plus 25%, so the saving cannot be undone
without failing the job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vishnuv688
vishnuv688 changed the base branch from feat/494-hosted-trace-player to main November 2, 2026 09:05
@vishnuv688
vishnuv688 merged commit d94e95b into main Nov 2, 2026
9 checks passed
@vishnuv688
vishnuv688 deleted the perf/495-frame-snapshot-dedupe branch November 2, 2026 09:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Trace: stop storing each action screenshot twice

1 participant